/**
 * @author Caius
 * @description
 *      股票服务实现
 * @version 1.0
 * @since Created in 2025-01-03
 */

package com.itsense.stock.service.Impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itsense.stock.mapper.*;
import com.itsense.stock.pojo.domain.*;
import com.itsense.stock.pojo.vo.StockInfoConfig;
import com.itsense.stock.service.StockService;
import com.itsense.stock.utils.DateTimeUtil;
import com.itsense.stock.vo.resp.PageResult;
import com.itsense.stock.vo.resp.R;
import com.itsense.stock.vo.resp.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StockServiceImpl implements StockService {
    @Qualifier("stock-com.itsense.stock.pojo.vo.StockInfoConfig")
    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;
    @Autowired
    private Cache<String, Object> coffeineCache;
    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;
    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    /**
     * 获取大盘最新数据
     *
     * @return
     */
    @Override
    public R<List<InnerMarketDomain>> getInnerMarketInfo() {

        R<List<InnerMarketDomain>> result = (R<List<InnerMarketDomain>>) coffeineCache.get("innerMarketKey", key -> {
            //1.获取股票最新的交易时间点(紧精确到分钟)
            Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            //mock date 等后续完成job工程之后才能完善这个数据(假数据)
            curDate = DateTime.parse("2021-12-28 09:31:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
            //2.获取大盘编码集合
            List<String> mCoders = stockInfoConfig.getInner();
            //3.调用mapper查询数据
            List<InnerMarketDomain> data = stockMarketIndexInfoMapper.getMarketInfo(curDate, mCoders);
            //4.封装并响应
            return R.ok(data);
        });
        return result;
    }

    /**
     * 需求: 获取深沪两市的板块时分行情进行数据查询
     *
     * @return
     */
    @Override
    public R<List<StockBlockDomain>> getStockBlockInfo() {
        //获取交易时间点
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //Mock 数据之后删除
        curDate = DateTime.parse("2021-12-21 14:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //使用mapper接口获取数据
        List<StockBlockDomain> infos = stockBlockRtInfoMapper.getStockBlockInfo(curDate);
        //封装数据
        if (CollectionUtil.isEmpty(infos)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(infos);
    }

    @Override
    public R<PageResult<StockUpDownDomain>> getStockInfoByPage(Integer page, Integer pageSize) {
        //获取交易时间点
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //Mock 数据之后删除
        curDate = DateTime.parse("2021-12-30 09:42:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //设置pagehelper分页参数
        PageHelper.startPage(page, pageSize);
        //调用mapper查询
        List<StockUpDownDomain> pageData = stockRtInfoMapper.getStockInfoByTime(curDate);
        //组装pageResult对象
        PageInfo<StockUpDownDomain> pageInfo = new PageInfo<>(pageData);
        PageResult<StockUpDownDomain> pageResult = new PageResult<>(pageInfo);

        return R.ok(pageResult);
    }

    @Override
    public R<List<StockUpDownDomain>> getStockInfoIncrease() {
        //获取交易时间点
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //Mock 数据之后删除
        curDate = DateTime.parse("2021-12-30 09:42:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //调用mapper查询
        List<StockUpDownDomain> showData = stockRtInfoMapper.getStockInfoByTimeLimit(curDate);
        //组装返回数据
        return R.ok(showData);

    }

    /**
     * 返回最新涨跌数据
     *
     * @return
     */
    @Override
    public R<Map<String, List>> getStockUpDownCount() {
        //获取交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //假数据
        curDateTime = DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = curDateTime.toDate();
        //获取最新开盘时间
        Date startDate = DateTimeUtil.getOpenDate(curDateTime).toDate();
        //3.统计涨停数据
        List<Map> upList = stockRtInfoMapper.getStockUpDownCount(startDate, endDate, 1);
        //4.统计跌停数据
        List<Map> downList = stockRtInfoMapper.getStockUpDownCount(startDate, endDate, 0);
        //封装返回数据
        HashMap<String, List> info = new HashMap<>();
        info.put("upList", upList);
        info.put("downList", downList);
        return R.ok(info);
    }

    /**
     * 导出指定页码的最新股票信息
     *
     * @param page
     * @param pageSize
     * @param response
     */
    @Override
    public void exportStockUpDownInfo(Integer page, Integer pageSize, HttpServletResponse response) {
        //1.获取分页数据
        R<PageResult<StockUpDownDomain>> r = this.getStockInfoByPage(page, pageSize);
        List<StockUpDownDomain> rows = r.getData().getRows();
        //2.导出数据
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        try {
            String fileName = URLEncoder.encode("股票信息表", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), StockUpDownDomain.class).sheet("股票涨幅信息").doWrite(rows);
        } catch (IOException e) {
            log.error("当前页码: {}, 每页大小: {}, 当前信息: {}, 异常信息{}", page, pageSize, DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), e.getMessage());
        }
        //通知前端异常,稍后重试
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        R<Object> error = R.error(ResponseCode.ERROR);
        try {
            String jsonData = new ObjectMapper().writeValueAsString(error);
            response.getWriter().write(jsonData);
        } catch (IOException e) {
            log.error("exportStockUpDownInfo: 响应错误信息失败, 当前时间:{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
        }

    }

    /**
     * 返回T日和T-1日的每分钟交易量的统计
     *
     * @return
     */
    @Override
    public R<Map<String, List>> getComparedStockTradeAmt() {
        //1.获取T日(最新的交易日的日期范围)
        DateTime tEndDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //Mock Date
        tEndDateTime = DateTime.parse("2022-01-03 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date tEndDate = tEndDateTime.toDate();
        //开盘时间
        Date tStartDate = DateTimeUtil.getOpenDate(tEndDateTime).toDate();
        //2.获取T-1日的时间范围
        DateTime preTEndDateTime = DateTimeUtil.getPreviousTradingDay(tEndDateTime);
        //Mock Date
        preTEndDateTime = DateTime.parse("2022-01-02 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date preTEndDate = preTEndDateTime.toDate();
        //开盘时间
        Date tPreStartDate = DateTimeUtil.getOpenDate(preTEndDateTime).toDate();
        //3.调用mapper层
        //3.1返回T日的数据
        List<Map> tData = stockMarketIndexInfoMapper.getSumAmtInfo(tStartDate, tEndDate, stockInfoConfig.getInner());
        //3.2返回T-1日的数据
        List<Map> preTData = stockMarketIndexInfoMapper.getSumAmtInfo(tPreStartDate, preTEndDate, stockInfoConfig.getInner());
        //4.组装返回数据
        HashMap<String, List> info = new HashMap<>();
        info.put("amtList", tData);
        info.put("yesAmtList", preTData);
        //响应数据
        return R.ok(info);
    }

    /**
     * 获取股票的每日涨幅范围数据(A股)
     *
     * @return
     */
    @Override
    public R<Map> getIncreaseRangeInfo() {
        //1.获取最新时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //Mock Date
        curDateTime = DateTime.parse("2022-01-06 09:55:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2.调用mapper
        List<Map> infos = stockRtInfoMapper.getIncreaseRangeInfoByDate(curDate);
        //返回使用标签
        //way1
        List<String> titles = stockInfoConfig.getUpDownRange();
//        List<Map> allInfos = new ArrayList<>();
//        for (String title : titles) {
//            Map tmp = null;
//            for (Map info : infos) {
//                if (info.containsValue(title)) {
//                    tmp = info;
//                    break;
//                }
//            }
//            if (tmp == null) {
//                //不存在,补齐
//                tmp = new HashMap<>();
//                tmp.put("title", title);
//                tmp.put("count",0);
//            }
//            allInfos.add(tmp);
//        }
        //way2: 使用stream流实现
        List<Map> collect = titles.stream().map(title -> {
            Optional<Map> result = infos.stream().filter(map -> map.containsValue(title)).findFirst();
            if (result.isPresent()) {
                return result.get();
            } else {
                HashMap<String, Object> tmp = new HashMap<>();
                tmp.put("title", title);
                tmp.put("count", 0);
                return tmp;
            }
        }).collect(Collectors.toList());
        //3.组装数据
        HashMap<String, Object> data = new HashMap<>();
        data.put("time", curDateTime.toString("yyyy-MM-dd HH:mm:ss"));
        data.put("infos", collect);
        //4.响应数据
        return R.ok(data);
    }

    /**
     * 获取指定股票的T日的分时数据
     *
     * @param stockCode
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> getStockScreenTimeSharing(String stockCode) {
        //获取最新时间
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //MockTime数据
        endDateTime = DateTime.parse("2021-12-30 14:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = endDateTime.toDate();
        Date openDate = DateTimeUtil.getOpenDate(endDateTime).toDate();
        //2.查询
        List<Stock4MinuteDomain> data = stockRtInfoMapper.getStock4MinuteInfo(openDate, endDate, stockCode);
        //3.返回数据
        return R.ok(data);
    }

    /**
     * 统计指定股票的日K数据
     *
     * @param stockCode
     * @return
     */
    @Override
    public R<List<Stock4EvrDayDomain>> getStockScreenDKLine(String stockCode) {
        //1.获取统计日K线的数据的时间范围
        //1.1 获取截止时间
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //Mock Time
        endDateTime = DateTime.parse("2022-06-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = endDateTime.toDate();
        //1.2 起始时间
        DateTime startDateTime = endDateTime.minusMonths(3);
        startDateTime = DateTime.parse("2022-01-01 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date startDate = startDateTime.toDate();
        //调用mapper
        List<Stock4EvrDayDomain> dkLineData = stockRtInfoMapper.getStockDKLine(startDate, endDate, stockCode);
        //返回数据
        return R.ok(dkLineData);
    }

    /**
     * 获取国外大盘的最新数据
     *
     * @return
     */
    @Override
    public R<List<OuterMarketDomain>> getOuterMarketInfo() {
        R<List<OuterMarketDomain>> result = (R<List<OuterMarketDomain>>) coffeineCache.get("OuterMarketKey", key -> {
            //1.获取股票最新的交易时间点
            Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            //假数据
            curDate = DateTime.parse("2022-05-18 15:58:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
            //使用mapper查询对应的数据
            List<OuterMarketDomain> data = stockOuterMarketIndexInfoMapper.getMarketInfo(curDate);
            //返回数据
            return R.ok(data);
        });
        return result;
    }

    /**
     * 返回模糊搜索的股票数据
     *
     * @param searchStr
     * @return
     */
    @Override
    public R<List<StockSearchResultDomain>> getStockSearchResult(String searchStr) {
        //调用mapper返回对应的数据
        List<StockSearchResultDomain> result = stockBusinessMapper.getStockSearchResult(searchStr);

        return R.ok(result);
    }

    /**
     * 通过股票编码返回对应的主营业务信息
     *
     * @param code
     * @return
     */
    @Override
    public R<StockDescribeDomain> getStockDescribe(String code) {
        //直接使用mapper返回数据
        StockDescribeDomain data = stockBusinessMapper.getDescribeByCode(code);

        return R.ok(data);
    }

    /**
     * 通过股票的编码返回对应的周K线
     *
     * @param code
     * @return
     */
    @Override
    public R<List<Stock4EvrWeekDomain>> getStockWKLine(String code) {
        //获取当前时间
        DateTime curTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date curDate = curTime.toDate();
        Date startDate = curTime.minusDays(4).toDate();

        //mockDate
        startDate = DateTime.parse("2021-12-25 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        curDate = DateTime.parse("2021-12-30 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        //调用mapper实现获取对应的数据(注意传入参数实现对应的数据的传入操作)
        List<Stock4EvrWeekDomain> data = stockRtInfoMapper.getStockWKLine(code, startDate, curDate);

        return R.ok(data);
    }

    /**
     * 通过股票编码返回对应的股票实时数据
     *
     * @param code
     * @return
     */
    @Override
    public R<StockRt> getStockScreenRtInfo(String code) {
        DateTime curDate = DateTimeUtil.getLastDate4Stock(DateTime.now());
        DateTime openDate = DateTimeUtil.getOpenDate(curDate);

        Date endDate = curDate.toDate();
        Date startDate = openDate.toDate();
        // mock Date
        startDate = DateTime.parse("2021-12-30 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endDate = DateTime.parse("2021-12-30 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //使用mapper返回对应的数据
        StockRt data = stockRtInfoMapper.getStockRt(code, startDate, endDate);

        return R.ok(data);
    }

    /**
     * 通过股票编码返回个股行情数据
     * @param code
     * @return
     */
    @Override
    public R<List<Map<String, Object>>> getStockTradeInStatement(String code) {
        if (code == null || code.isEmpty()) {
            return R.error(ResponseCode.DATA_ERROR.getMessage());
        }
        //判断是否是空值之后使用mapper返回对应的数据
        List<Map<String, Object>>data = stockRtInfoMapper.getStockTradeInStatement(code);

        return R.ok(data);
    }
}
